热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

封闭性|加法器_带你玩转多进程编程

篇首语:本文由编程笔记#小编为大家整理,主要介绍了带你玩转多进程编程相关的知识,希望对你有一定的参考价值。前言之前用加法器的例子一文带你轻松掌

篇首语:本文由编程笔记#小编为大家整理,主要介绍了带你玩转多进程编程相关的知识,希望对你有一定的参考价值。



前言

之前用加法器的例子一文带你轻松掌握多种范式讲解了多种范式的封装差异得到了很多童鞋的阅读。这次我再通过对进程的封装来给大家继续加深讲解下关于这几种编程范式的差异吧。


结构化设计

相对于pthread_create()函数,fork函数要弱一些。不过不要紧我们马上把它加强一下。

#include <iostream>
#include
#include
#include
using namespace std;
typedef void (*PROCESS)(void *pContext);
pid_t CreateProcess(PROCESS child, void *pContext)
pid_t pid &#61; fork();

if(0 &#61;&#61; pid)

child(pContext);
exit(0);

else

return pid;

void MyChild(void *pContext)
long i &#61; (long)pContext;
cout <<"In child: " <int main()
long i &#61; 94;
CreateProcess(MyChild, (void *)i);

cout <<"In Father" < return 0;

我们首先来看看main函数&#xff0c;父进程调用了 CreateProcess函数&#xff0c;该函数传递了两个参数&#xff0c;即函数地址MyChild和自定义整型变量i。CreateProcess函数接受两个参数&#xff0c;一个是PROCESS类型的child,二是一个void类型的指针。后者实际上就是父进程传递给子进程的自定义参数。类似于pthread_create的第四个参数&#xff0c;二前者PROCESS是一个函数指针的类型&#xff0c;它规定函数是一个无返回值&#xff0c;且只接收一个void*的参数。实际上&#xff0c;新建进程也是就是子进程的业务逻辑&#xff0c;就应该被封装在这个参数对应的函数中。
好了&#xff0c; 下面来看看CreateProcess函数首先调用了fork函数创建子进程&#xff0c;接着判断fork函数的返回值是否为0&#xff0c;即判断是否处于子进程中运行。若是的话则调用child函数&#xff0c;这里的child实际上就是MyChild&#xff0c;这样一来子进程跳转到MyChild函数开始运行&#xff0c;在该函数中&#xff0c;子进程将接收都的参数打印出来。
CreateProcess函数就是我们按照结构化设计思想封装fork函数的结果&#xff0c;当然也是我们模仿pthread_create函数的结果。使用CreateProcess的函数显而易见。很多同学在学习fork的时候&#xff0c;都记不清楚fork函数是在父进程中返回0&#xff0c;还是在子进程中返回0.使用CreateProcess函数能够有效地封装变化点。要是子进程想要换一个业务逻辑执行&#xff0c;只需要编写一个封装了该逻辑的函数并使这个函数满足原型要求即可&#xff0c;当然CreateProcess也有自己的绝限性&#xff0c;这个局限性和很多结构化编程范式的一样&#xff0c;没有将数据和操作数据的方式结合起来。比如&#xff0c;当创建完子进程后&#xff0c;需要等待它死亡&#xff0c;此时该怎么做呢&#xff1f;父进程调用完CreateProcess函数后&#xff0c;需要保留它的返回值。正常情况下&#xff0c;这个返回值就是子进程的ID。然后希望子进程在死亡的时候&#xff0c;以之前的ID调用waitpid函数&#xff0c;在这个过程中父进程需要保存子进程的ID&#xff0c;如果ID能和操作子进程的相关函数关联在一起对于开发者来说是十分方便的。可惜的是&#xff0c;按照结构化的程序设计思想封装的结果&#xff0c;目前还不能做到这一点。


基于对象的设计

既然我们已经提到了CreateProcess函数的局限性&#xff0c;那就是没有办法把进程相关的数据和操作进程的方法结合起来。而基于对象的方法恰好就能满足这一要求。


class Process
public:
Process();
~Process();
int Run(void *pContext);
int WaitForDeath();
private:
int StartFunctionOfProcess(void *pContext);
private:
Process(const Process&);
Process& operator&#61;(const Process&);
private:
pid_t m_ProcessID;
;

这里我们给出了 Process类的声明&#xff0c;从声明可知&#xff0c; Process类有一个成员变量m_ProcessID&#xff0c;即子进程的ID。另外还有三个成员函数&#xff0c;即公开的Run方法&#xff0c;WaitForDeath方法以及私有的StartFunctionOfProcess方法。Run方法为了创建新进程&#xff0c;而WaitForDeath方法即为了等待新进程死亡。那么&#xff0c;这个StartFunctionOfProcess方法又有什么用途呢&#xff1f;我们通过Run方法的实现来看一看


int Process::Run(void *pContext)
m_ProcessID &#61; fork();
if(m_ProcessID &#61;&#61; 0)

m_ProcessID &#61; getpid();
StartFunctionOfProcess(pContext);
exit(0);

else if(m_ProcessID &#61;&#61; -1)

return -1;

else
return 0;

在Run方法的实现中&#xff0c;父进程调用了fork函数创建子进程&#xff0c;并将子进程的ID保存在m_ProcessID中&#xff0c;而子进程中则调用getpid函数来保存自己的ID&#xff0c;然后调用了StartFunctionOfProcess函数进行具体的业务处理&#xff0c;Run方法和StartFunctionOfProcess方法都有一个void*类型的参数&#xff0c;即让进程创建者可以传递自定义参数给子进程。而WaitForDeath函数比较简单&#xff0c;该函数简单地调用了waitpid函数&#xff0c;等待子进程的死亡。定义如下&#xff1a;

int Process::WaitForDeath()
if(m_ProcessID &#61;&#61; -1)
return -1;
if(waitpid(m_ProcessID, 0, 0) &#61;&#61; -1)
return -1;
else
return 0;

同结构化代码相比&#xff0c;这个类的封装实现了数据和操作数据的方法的封装。在这里&#xff0c;也就是将子进程ID和操作子进程的方法封装在了一起。比如当父进程需要等待子进程死亡时&#xff0c;只需要调用WaitForDeath函数即可&#xff0c;不用自己保存进程ID并以此waitpid函数。当然基于对象也有很大的缺陷&#xff0c;也就是面对进程需要执行不同业务逻辑这一变化点时&#xff0c;无法保证代码的封闭性。StartFunctionOfProcess函数封装了子进程将要执行的业务逻辑&#xff0c;当需要引入其它业务逻辑时&#xff0c;必将导致该函数被修改&#xff0c;代码的封闭性也就被破坏了。这个时候&#xff0c;面向对象的封装就登场了。


面向对象的封装

面向对象版本的进程封装就是将包装进程的业务逻辑函数StartFunctionOfProcess改为纯虚函数。由Process的派生类重写。这样一来&#xff0c;新进程的业务逻辑就不再放到Process中了&#xff0c;而是放到派生类中。当需要添加新的业务逻辑时&#xff0c;只需要增加Process的派生类即可&#xff0c;无需再改Process。下面来看看代码

class Process
public:
Process();
virtual ~Process();
virtual int Run(void *pContext);
virtual int WaitForDeath();
protected:
virtual int StartFunctionOfProcess(void *pContext) &#61; 0;
private:
Process(const Process&);
Process& operator&#61;(const Process&);
protected:
pid_t m_ProcessID;
;

面向对象版本的Process类的声明同之前相比变化不大&#xff0c;主要是将StartFunctionOfProcess函数编程了纯虚函数。该函数是进程的入口函数&#xff0c;封装了子进程需要执行的业务逻辑。当需要增加子进程执行的业务逻辑时&#xff0c;可以按照下面的例子重写纯虚函数StartFunctionOfProcess即可&#xff0c;无需修改基类Process,从而满足了代码的封闭性。

class MyProcess : public Process
public:
virtual int StartFunctionOfProcess(void *pContext)

cout <<(long)pContext < return 0;

;

结尾

可能大家觉得上面这个例子太简单了&#xff0c;不过瘾。没关系&#xff0c;下一篇文章通过一个多进程的生产消费的实例让大家更能深入理解多进程开发。


推荐阅读
  • 作者一直强调的一个概念叫做oneloopperthread,撇开多线程不谈,本篇博文将学习,怎么将传统的IO复用pollepoll封装到C++类中。1.IO复用复习使用p ... [详细]
  • 线程漫谈——线程基础
    本系列意在记录Windwos线程的相关知识点,包括线程基础、线程调度、线程同步、TLS、线程池等。进程与线程理解线程是至关重要的,每个进程至少有一个线程,进程是线程的容器,线程才是真正的执行体,线程必 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 第七课主要内容:多进程多线程FIFO,LIFO,优先队列线程局部变量进程与线程的选择线程池异步IO概念及twisted案例股票数据抓取 ... [详细]
  • UNIX高级环境编程 第11、12章 线程及其属性
    第11章线程11.2线程概念线程资源:线程ID,一组寄存器,栈,调度优先级和策略,信号屏蔽字,e ... [详细]
  • Linux线程的同步和互斥
    目录1、线程的互斥2、可重入VS线程安全3、线程的同步1、线程的互斥 ... [详细]
  • 主线:设计窗口类注册窗口类产生窗口显示窗口更新窗口消息循环(将消息路由到窗口中去处理)。APPMODUL.CPP源文件被编译链接进入项目,从APPMOD ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了一个线程多次调用一个函数相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
author-avatar
xin丶儿_462
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有